#pragma once

#include "../umlmodel_global.h"

#include <memory>
#include <vector>

#include "AggregationKind.h"
#include "ConnectableElement.h"
#include "DeploymentTarget.h"
#include "StructuralFeature.h"

using std::make_shared;
using std::shared_ptr;
using std::vector;

namespace uml {
class Association;
class Class;
class DataType;
class Interface;
class ParameterableElement;
class RedefinableElement;
class Type;
class ValueSpecification;

/// <summary>
/// Property
/// A Property is a StructuralFeature. A Property related by ownedAttribute to a
/// Classifier (other than an association) represents an attribute and might also
/// represent an association end. It relates an instance of the Classifier to a
/// value or set of values of the type of the attribute. A Property related by
/// memberEnd to an Association represents an end of the Association. The type of
/// the Property is the type of the end of the Association. A Property has the
/// capability of being a DeploymentTarget in a Deployment relationship. This
/// enables modeling the deployment to hierarchical nodes that have Properties
/// functioning as internal parts.  Property specializes ParameterableElement to
/// specify that a Property can be exposed as a formal template parameter, and
/// provided as an actual parameter in a binding of a template.
/// </summary>
class UMLMODEL_EXPORT Property : public ConnectableElement, public StructuralFeature, public DeploymentTarget {

public: // Constructors and destructors
    explicit Property(const QString& umlType="uml::Property");

public: // JsonSerializable override
    void fromJson(const QJsonObject& json, QStringList& errorList) override;
    void toJson(QJsonObject& json) override;

public: // Methods
    /// <summary>
    /// isAttribute
    /// The query isAttribute() is true if the Property is defined as an attribute of
    /// some Classifier.
    /// </summary>
    virtual bool isAttribute() const;

    /// <summary>
    /// isCompatibleWith
    /// The query isCompatibleWith() determines if this Property is compatible with the
    /// specified ParameterableElement. This Property is compatible with
    /// ParameterableElement p if the kind of this Property is thesame as or a subtype
    /// of the kind of p. Further, if p is a TypedElement, then the type of this
    /// Property must be conformant with the type of p.
    /// </summary>
    bool isCompatibleWith(shared_ptr<ParameterableElement> p) const override;

    /// <summary>
    /// isConsistentWith
    /// The query isConsistentWith() specifies, for any two Properties in a context in
    /// which redefinition is possible, whether redefinition would be logically
    /// consistent. A redefining Property is consistent with a redefined Property if the
    /// type of the redefining Property conforms to the type of the redefined Property,
    /// and the multiplicity of the redefining Property (if specified) is contained in
    /// the multiplicity of the redefined Property.
    /// </summary>
    bool isConsistentWith(shared_ptr<RedefinableElement> redefiningElement) const override;

    /// <summary>
    /// isNavigable
    /// The query isNavigable() indicates whether it is possible to navigate across the
    /// property.
    /// </summary>
    virtual bool isNavigable() const;

    /// <summary>
    /// subsettingContexts
    /// The query subsettingContext() gives the context for subsetting a Property. It
    /// consists, in the case of an attribute, of the corresponding Classifier, and in
    /// the case of an association end, all of the Classifiers at the other ends.
    /// </summary>
    virtual shared_ptr<vector<Type>> subsettingContexts() const;

public: // Properties
    /// <summary>
    /// aggregation getter
    /// Specifies the kind of aggregation that applies to the Property.
    /// </summary>
    AggregationKind aggregation() const;

    /// <summary>
    /// aggregation setter
    /// Specifies the kind of aggregation that applies to the Property.
    /// </summary>
    void setAggregation(AggregationKind value);

    /// <summary>
    /// association getter
    /// The Association of which this Property is a member, if any.
    /// </summary>
    shared_ptr<Association> association() const;

    /// <summary>
    /// association setter
    /// The Association of which this Property is a member, if any.
    /// </summary>
    void setAssociation(shared_ptr<Association> value);

    /// <summary>
    /// associationEnd getter
    /// Designates the optional association end that owns a qualifier attribute.
    /// </summary>
    shared_ptr<Property> associationEnd() const;

    /// <summary>
    /// associationEnd setter
    /// Designates the optional association end that owns a qualifier attribute.
    /// </summary>
    void setAssociationEnd(shared_ptr<Property> value);

    /// <summary>
    /// class2 getter
    /// The Class that owns this Property, if any.
    /// </summary>
    shared_ptr<Class> class2() const;

    /// <summary>
    /// class2 setter
    /// The Class that owns this Property, if any.
    /// </summary>
    void setClass2(shared_ptr<Class> value);

    /// <summary>
    /// datatype getter
    /// The DataType that owns this Property, if any.
    /// </summary>
    shared_ptr<DataType> datatype() const;

    /// <summary>
    /// datatype setter
    /// The DataType that owns this Property, if any.
    /// </summary>
    void setDatatype(shared_ptr<DataType> value);

    /// <summary>
    /// defaultValue getter
    /// A ValueSpecification that is evaluated to give a default value for the Property
    /// when an instance of the owning Classifier is instantiated.
    /// </summary>
    shared_ptr<ValueSpecification> defaultValue() const;

    /// <summary>
    /// defaultValue setter
    /// A ValueSpecification that is evaluated to give a default value for the Property
    /// when an instance of the owning Classifier is instantiated.
    /// </summary>
    void setDefaultValue(shared_ptr<ValueSpecification> value);

    /// <summary>
    /// interface2 getter
    /// The Interface that owns this Property, if any.
    /// </summary>
    shared_ptr<Interface> interface2() const;

    /// <summary>
    /// interface2 setter
    /// The Interface that owns this Property, if any.
    /// </summary>
    void setInterface2(shared_ptr<Interface> value);

    /// <summary>
    /// isComposite getter
    /// If isComposite is true, the object containing the attribute is a container for
    /// the object or value contained in the attribute. This is a derived value,
    /// indicating whether the aggregation of the Property is composite or not.
    /// </summary>
    bool isComposite() const;

    /// <summary>
    /// isComposite setter
    /// If isComposite is true, the object containing the attribute is a container for
    /// the object or value contained in the attribute. This is a derived value,
    /// indicating whether the aggregation of the Property is composite or not.
    /// </summary>
    void setIsComposite(bool value);

    /// <summary>
    /// isDerived getter
    /// Specifies whether the Property is derived, i.e., whether its value or values can
    /// be computed from other information.
    /// </summary>
    bool isDerived() const;

    /// <summary>
    /// isDerived setter
    /// Specifies whether the Property is derived, i.e., whether its value or values can
    /// be computed from other information.
    /// </summary>
    void setIsDerived(bool value);

    /// <summary>
    /// isDerivedUnion getter
    /// Specifies whether the property is derived as the union of all of the Properties
    /// that are constrained to subset it.
    /// </summary>
    bool isDerivedUnion() const;

    /// <summary>
    /// isDerivedUnion setter
    /// Specifies whether the property is derived as the union of all of the Properties
    /// that are constrained to subset it.
    /// </summary>
    void setIsDerivedUnion(bool value);

    /// <summary>
    /// isID getter
    /// True indicates this property can be used to uniquely identify an instance of the
    /// containing Class.
    /// </summary>
    bool isID() const;

    /// <summary>
    /// isID setter
    /// True indicates this property can be used to uniquely identify an instance of the
    /// containing Class.
    /// </summary>
    void setIsID(bool value);

    /// <summary>
    /// opposite getter
    /// In the case where the Property is one end of a binary association this gives the
    /// other end.
    /// </summary>
    shared_ptr<Property> opposite() const;

    /// <summary>
    /// opposite setter
    /// In the case where the Property is one end of a binary association this gives the
    /// other end.
    /// </summary>
    void setOpposite(shared_ptr<Property> value);

    /// <summary>
    /// owningAssociation getter
    /// The owning association of this property, if any.
    /// </summary>
    shared_ptr<Association> owningAssociation() const;

    /// <summary>
    /// owningAssociation setter
    /// The owning association of this property, if any.
    /// </summary>
    void setOwningAssociation(shared_ptr<Association> value);

    /// <summary>
    /// qualifiers getter
    /// An optional list of ordered qualifier attributes for the end.
    /// </summary>
    vector<shared_ptr<Property>>& qualifiers();

    /// <summary>
    /// redefinedPropertys getter
    /// The properties that are redefined by this property, if any.
    /// </summary>
    vector<shared_ptr<Property>>& redefinedPropertys();

    /// <summary>
    /// subsettedPropertys getter
    /// The properties of which this Property is constrained to be a subset, if any.
    /// </summary>
    vector<shared_ptr<Property>>& subsettedPropertys();

private: // Fields
    /// <summary>
    /// aggregation
    /// Specifies the kind of aggregation that applies to the Property.
    /// </summary>
    AggregationKind aggregation_ = AggregationKind::knone;

    /// <summary>
    /// association
    /// The Association of which this Property is a member, if any.
    /// </summary>
    shared_ptr<Association> association_;

    /// <summary>
    /// associationEnd
    /// Designates the optional association end that owns a qualifier attribute.
    /// </summary>
    shared_ptr<Property> associationEnd_;

    /// <summary>
    /// class2
    /// The Class that owns this Property, if any.
    /// </summary>
    shared_ptr<Class> class2_;

    /// <summary>
    /// datatype
    /// The DataType that owns this Property, if any.
    /// </summary>
    shared_ptr<DataType> datatype_;

    /// <summary>
    /// defaultValue
    /// A ValueSpecification that is evaluated to give a default value for the Property
    /// when an instance of the owning Classifier is instantiated.
    /// </summary>
    shared_ptr<ValueSpecification> defaultValue_;

    /// <summary>
    /// interface2
    /// The Interface that owns this Property, if any.
    /// </summary>
    shared_ptr<Interface> interface2_;

    /// <summary>
    /// isComposite
    /// If isComposite is true, the object containing the attribute is a container for
    /// the object or value contained in the attribute. This is a derived value,
    /// indicating whether the aggregation of the Property is composite or not.
    /// </summary>
    const static bool isComposite_default_;
    bool isComposite_= isComposite_default_;

    /// <summary>
    /// isDerived
    /// Specifies whether the Property is derived, i.e., whether its value or values can
    /// be computed from other information.
    /// </summary>
    const static bool isDerived_default_;
    bool isDerived_= isDerived_default_;

    /// <summary>
    /// isDerivedUnion
    /// Specifies whether the property is derived as the union of all of the Properties
    /// that are constrained to subset it.
    /// </summary>
    const static bool isDerivedUnion_default_;
    bool isDerivedUnion_= isDerivedUnion_default_;

    /// <summary>
    /// isID
    /// True indicates this property can be used to uniquely identify an instance of the
    /// containing Class.
    /// </summary>
    const static bool isID_default_;
    bool isID_= isID_default_;

    /// <summary>
    /// opposite
    /// In the case where the Property is one end of a binary association this gives the
    /// other end.
    /// </summary>
    shared_ptr<Property> opposite_;

    /// <summary>
    /// owningAssociation
    /// The owning association of this property, if any.
    /// </summary>
    shared_ptr<Association> owningAssociation_;

    /// <summary>
    /// qualifiers
    /// An optional list of ordered qualifier attributes for the end.
    /// </summary>
    vector<shared_ptr<Property>> qualifiers_;

    /// <summary>
    /// redefinedPropertys
    /// The properties that are redefined by this property, if any.
    /// </summary>
    vector<shared_ptr<Property>> redefinedPropertys_;

    /// <summary>
    /// subsettedPropertys
    /// The properties of which this Property is constrained to be a subset, if any.
    /// </summary>
    vector<shared_ptr<Property>> subsettedPropertys_;

};

} // namespace uml
